/*
 * Copyright (c) 2007, TopCoder, Inc. All rights reserved.
 */
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using TopCoder.Util.Calendar.Entry;
using TopCoder.WinForms.Controls.CalendarView.Views;

namespace TopCoder.WinForms.Controls.CalendarView
{
    /// <summary>
    /// <para>Provides utility methods.</para>
    /// </summary>
    /// <author>Ghostar</author>
    /// <author>yext</author>
    /// <author>argolite</author>
    /// <author>alex.dolin</author>
    /// <version>1.1</version>
    /// <copyright>Copyright (c) 2007, TopCoder, Inc. All rights reserved.</copyright>
    public static class Helper
    {
        /// <summary>
        /// The default font name.
        /// </summary>
        internal const string DEFAULT_FONT_NAME = "Arial";

        /// <summary>
        /// The format string for month name.
        /// </summary>
        internal const string MONTH_NAME_FORMAT_STRING = "y";

        /// <summary>
        /// The format string for each day in week.
        /// </summary>
        internal const string WEEKDAY_FORMAT_STRING = "ddd";

        /// <summary>
        /// The pen settings for border of the cells.
        /// </summary>
        internal static readonly Pen borderPen = new Pen(Color.LightGray);

        /// <summary>
        /// The background brush for cells.
        /// </summary>
        internal static readonly Brush normalBackBrush = new SolidBrush(Color.WhiteSmoke);

        /// <summary>
        /// The background brush for cells for today.
        /// </summary>
        private static readonly Brush todayBackBrush = new SolidBrush(Color.LemonChiffon);

        /// <summary>
        /// The background brush for the months.
        /// </summary>
        private static readonly Brush otherMonthBackBrush = new SolidBrush(Color.Gainsboro);

        /// <summary>
        /// The brush settings for the date text.
        /// </summary>
        private static readonly Brush dateTextBrush = new SolidBrush(Color.Black);

        /// <summary>
        /// The brush settings for background of the header of months.
        /// </summary>
        internal static readonly Brush headerBackBrush = new SolidBrush(Color.LightGray);

        /// <summary>
        /// The brush settings for text of the header of months.
        /// </summary>
        internal static readonly Brush headerBrush = new SolidBrush(Color.Gray);

        /// <summary>
        /// The highlighted background brush for this month.
        /// </summary>
        internal static readonly Brush thisMonthBrush = new SolidBrush(Color.LemonChiffon);

        /// <summary>
        /// String format for truncated strings in the calendar views.
        /// </summary>
        internal static readonly StringFormat StringTrimFormat = new StringFormat();

        /// <summary>
        /// Initializes static member of this class
        /// </summary>
        static Helper()
        {
            StringTrimFormat.Trimming = StringTrimming.EllipsisCharacter;
            StringTrimFormat.FormatFlags = StringFormatFlags.NoWrap;
        }

        /// <summary>
        /// <para>Returns a filtered list of <see cref="CalendarEntry"/> in which all entries are between
        /// <paramref name="startDate"/> and <paramref name="endDate"/> (inclusive).</para>
        /// </summary>
        /// <remarks>
        /// This helper method is used by all of the views.
        /// </remarks>
        /// <param name="allData">The entires to be filtered.</param>
        /// <param name="startDate">The start date.</param>
        /// <param name="endDate">The end date.</param>
        /// <returns>The filtered entries list.</returns>
        public static IList<CalendarEntry> SelectEntriesByDateRange(IList<CalendarEntry> allData,
                                                                        DateTime startDate, DateTime endDate)
        {
            IList<CalendarEntry> result = new List<CalendarEntry>();
            foreach (CalendarEntry entry in allData)
            {
                if (entry.Date.Date >= startDate && entry.Date.Date <= endDate)
                {
                    result.Add(entry);
                }
            }

            SortEntriesByDateTime((List<CalendarEntry>)result);            

            return result;
        }

        public static IList<CalendarEntry> SelectEntriesByDateTimeRange(IList<CalendarEntry> allData,
                                                                DateTime startDate, DateTime endDate)
        {
            IList<CalendarEntry> result = new List<CalendarEntry>();
            foreach (CalendarEntry entry in allData)
            {
                if (entry.Date >= startDate && entry.Date <= endDate)
                {
                    result.Add(entry);
                }
            }

            SortEntriesByDateTime((List<CalendarEntry>)result);

            return result;
        }

        /// <summary>
        /// <para>This helper method draws a month view in the drawing panel.</para>
        /// </summary>
        /// <remarks>
        /// This method is used by <see cref="QuarterlyCalendarView"/> and <see cref="YearlyCalendarView"/>.
        /// </remarks>
        /// <param name="view">The calendar view.</param>
        /// <param name="drawingPanel">The drawing panel.</param>
        /// <param name="monthViewType">The month view type.</param>
        /// <param name="weekViewType">The week view type.</param>
        /// <param name="dayViewType">The day view type.</param>
        /// <param name="columns">The list of columns to display.</param>
        /// <param name="sortedEntries">The sorted entries.</param>
        /// <param name="sortedEntryTypes">The sorted entry types.</param>
        /// <param name="date">The first day of the month.</param>
        /// <param name="x">The x position of the month view.</param>
        /// <param name="y">The y position of the month view.</param>
        /// <param name="columnWidth">The width of each column in the view.</param>
        /// <param name="rowHeight">The height of each data row in the view.</param>
        /// <param name="highlightMonth">Whether the current month should be highlighted.</param>
        /// <param name="monthNameFontSize">The font size for the name of the month.</param>
        /// <param name="headerFontSize">The font size for the names of the days.</param>
        /// <param name="headerRowHeight">The height of the month name row.</param>
        /// <param name="textMargin">The margin value of the text.</param>
        /// <param name="dateLabelHeight">The height of the date label.</param>
        /// <param name="defaultBarHeight">The default height of each color bar.</param>
        public static void DrawMonth(
            AbstractCalendarView view,
            Panel drawingPanel,
            string monthViewType,
            string weekViewType,
            string dayViewType,
            IList<string> columns,
            IDictionary<DateTime, IList<CalendarEntry>> sortedEntries,
            IDictionary<DateTime, IDictionary<CalendarEntryType, int>> sortedEntryTypes,
            DateTime date,
            float x, float y, float columnWidth, float rowHeight,
            bool highlightMonth,
            int monthNameFontSize,
            int headerFontSize,
            int headerRowHeight,
            int textMargin,
            int dateLabelHeight,
            int defaultBarHeight)
        {
            // The header:
            float totalWidth = columnWidth*7;

            string monthPanelName = date.ToString(MONTH_NAME_FORMAT_STRING);
            Panel monthPanel = drawingPanel.Controls[monthPanelName] as Panel;

            if (monthPanel == null)
            {
                monthPanel = new NavigationPanel(view, monthViewType, date);
                monthPanel.Name = monthPanelName;
                monthPanel.Parent = drawingPanel;
                monthPanel.Paint +=
                    new MonthHeaderPaintHandler(date, highlightMonth, monthNameFontSize, textMargin)
                        .OnPaint;
            }
            UpdatePanel(monthPanel, (int)Math.Ceiling(x), (int)Math.Ceiling(y),
                (int)Math.Ceiling(totalWidth), headerRowHeight);

            y += headerRowHeight;

            DateTime viewDate = date.AddDays(-((int)date.DayOfWeek));

            // Days of the week:
            string weekPanelName = monthPanelName + "Week";
            Panel weekPanel = drawingPanel.Controls[weekPanelName] as Panel;

            if (weekPanel == null)
            {
                weekPanel = new NavigationPanel(view, weekViewType, date);
                weekPanel.Name = weekPanelName;
                weekPanel.Parent = drawingPanel;
                weekPanel.Paint +=
                    new WeekdaysPaintHandler(viewDate, headerRowHeight, headerFontSize, textMargin).OnPaint;
            }
            UpdatePanel(weekPanel, (int)Math.Ceiling(x), (int)Math.Ceiling(y),
                (int)Math.Ceiling(totalWidth), headerRowHeight);

            y += headerRowHeight;

            for (int i = 0; i < 6; i++)
            {
                for (int j = 0; j < 7; j++)
                {
                    DrawCell(view, drawingPanel, dayViewType, columns, sortedEntries, sortedEntryTypes, viewDate,
                             x + j*columnWidth, y + i*rowHeight,
                             columnWidth, rowHeight, date, headerFontSize, textMargin, dateLabelHeight,
                             defaultBarHeight);
                    viewDate = viewDate.AddDays(1);
                }
            }
        }

        /// <summary>
        /// <para>This private method draws a cell for a day in the drawing panel.</para>
        /// </summary>
        /// <remarks>
        /// This method is used by <see cref="QuarterlyCalendarView"/> and <see cref="YearlyCalendarView"/>.
        /// </remarks>
        /// <param name="view">The caller of this method.</param>
        /// <param name="drawingPanel">The drawing panel.</param>
        /// <param name="dayViewType">The day view type.</param>
        /// <param name="columns">The list of columns to display.</param>
        /// <param name="sortedEntries">The sorted entries.</param>
        /// <param name="sortedEntryTypes">The dictionary containing entry types sorted by date.</param>
        /// <param name="date">The date of the cell.</param>
        /// <param name="x">The x position of the cell.</param>
        /// <param name="y">The y position of the cell.</param>
        /// <param name="width">The width of the cell.</param>
        /// <param name="height">The height of the cell.</param>
        /// <param name="startDate">The start date of the month.</param>
        /// <param name="dateFontSize">The font size of the date label.</param>
        /// <param name="dateLabelHeight">The height of the date label.</param>
        /// <param name="dateLabelMargin">The top and left margin of the date label.</param>
        /// <param name="defaultBarHeight">The default height of each color bar.</param>
        public static void DrawCell(
            AbstractCalendarView view,
            Panel drawingPanel,
            string dayViewType,
            IList<string> columns,
            IDictionary<DateTime, IList<CalendarEntry>> sortedEntries,
            IDictionary<DateTime, IDictionary<CalendarEntryType, int>> sortedEntryTypes,
            DateTime date, float x, float y, float width, float height,
            DateTime startDate,
            int dateFontSize,
            int dateLabelMargin,
            int dateLabelHeight,
            int defaultBarHeight)
        {
            Brush backColorBrush = normalBackBrush;
            if (date.Year != startDate.Year || date.Month != startDate.Month)
            {
                backColorBrush = otherMonthBackBrush;
            }
            else if (date == DateTime.Now.Date)
            {
                backColorBrush = todayBackBrush;
            }

            string dayPanelName = date.ToShortDateString() + (backColorBrush == otherMonthBackBrush ? "other" : "");
            Panel dayPanel = drawingPanel.Controls[dayPanelName] as Panel;

            if (dayPanel == null)
            {
                if(backColorBrush != otherMonthBackBrush && sortedEntries.ContainsKey(date))
                {
                    dayPanel = new FullDayInfoPopupPanel(sortedEntries[date], view, dayViewType, date);
                    ((FullDayInfoPopupPanel)dayPanel).Columns = columns;
                }
                else
                {
                    dayPanel = new NavigationPanel(view, dayViewType, date);
                }

                dayPanel.Name = dayPanelName;
                dayPanel.Parent = drawingPanel;
                dayPanel.Paint +=
                    new DayPaintHandler(dateFontSize, date, dateLabelMargin,
                                        backColorBrush, height - dateLabelHeight, dateLabelHeight, sortedEntryTypes,
                                        defaultBarHeight, view).OnPaint;
            }
            UpdatePanel(dayPanel, (int)Math.Ceiling(x), (int)Math.Ceiling(y),
                (int)Math.Ceiling(width), (int)Math.Ceiling(height));
        }

        /// <summary>
        /// Groups list of calendar entries by date.
        /// </summary>
        /// <param name="entries">List of calendar entries.</param>
        /// <returns>Dictionary of entries grouped by date.</returns>
        public static IDictionary<DateTime, IList<CalendarEntry>> GroupEntriesByDate(
            IList<CalendarEntry> entries)
        {
            IDictionary<DateTime, IList<CalendarEntry>> sortedEntries =
                new Dictionary<DateTime, IList<CalendarEntry>>();
            foreach (CalendarEntry entry in entries)
            {
                DateTime entryDate = entry.Date.Date;

                if (!sortedEntries.ContainsKey(entryDate))
                {
                    sortedEntries[entryDate] = new List<CalendarEntry>();
                }
                sortedEntries[entryDate].Add(entry);
            }

            foreach (DateTime dateTime in sortedEntries.Keys)
            {
                SortEntriesByDateTime((List<CalendarEntry>)sortedEntries[dateTime]);
            }

            return sortedEntries;
        }

        /// <summary>
        /// Groups list of calendar entries by type.
        /// </summary>
        /// <param name="entries">List of calendar entries.</param>
        /// <returns>Dictionary of entries grouped by type.</returns>
        public static IDictionary<CalendarEntryType, IList<CalendarEntry>> GroupEntriesByType(
            IList<CalendarEntry>  entries)
        {
            IDictionary<CalendarEntryType, IList<CalendarEntry>> sortedEntries =
                new Dictionary<CalendarEntryType, IList<CalendarEntry>>();
            foreach (CalendarEntry entry in entries)
            {
                if (!sortedEntries.ContainsKey(entry.Type))
                {
                    sortedEntries[entry.Type] = new List<CalendarEntry>();
                }
                sortedEntries[entry.Type].Add(entry);
            }

            foreach (CalendarEntryType entryType in sortedEntries.Keys)
            {                
                SortEntriesByDateTime((List<CalendarEntry>)sortedEntries[entryType]);
            }

            return sortedEntries;
        }

        private static void SortEntriesByDateTime(List<CalendarEntry> entries)
        {
            entries.Sort(
                delegate(CalendarEntry e1, CalendarEntry e2)
                {
                    if (e1.Date > e2.Date)
                        return 1;
                    if (e1.Date < e2.Date)
                        return -1;
                    return 0;
                });

        }

        /// <summary>
        /// <para>Returns a dictionary of sorted entry types by the date.</para>
        /// </summary>
        /// <remarks>
        /// This method is used by <see cref="QuarterlyCalendarView"/> and <see cref="YearlyCalendarView"/>.
        /// </remarks>
        /// <param name="currentData">The collection of entries.</param>
        /// <returns>The sorted entry types.</returns>
        public static IDictionary<DateTime, IDictionary<CalendarEntryType, int>> GetSortedEntryTypes(
            IList<CalendarEntry> currentData)
        {
            IDictionary<DateTime, IDictionary<CalendarEntryType, int>> result
                = new Dictionary<DateTime, IDictionary<CalendarEntryType, int>>();

            foreach (CalendarEntry entry in currentData)
            {
                DateTime entryDate = entry.Date.Date;
                if (!result.ContainsKey(entryDate))
                {
                    result[entryDate] = new Dictionary<CalendarEntryType, int>();
                }
                if (!result[entryDate].ContainsKey(entry.Type))
                {
                    result[entryDate][entry.Type] = 1;
                }
            }

            return result;
        }

        /// <summary>
        /// <para>Adjust the size of the DrawingPanel. If it's smaller than the HolderPanel, enlarge it,
        /// otherwise use scroll bars in the HolderPanel.</para>
        /// </summary>
        /// <remarks>
        /// This helper method is used by all of the views.
        /// </remarks>
        /// <param name="holderPanel">The holder panel, its size depends on the size of the control.</param>
        /// <param name="drawingPanel">The drawing panel.</param>
        /// <param name="initialWidth">The initially measured width.</param>
        /// <param name="initialHeight">The initially measured height.</param>
        /// <param name="widthRenderingStyle"></param>
        public static void AdjustPanelSize(Panel holderPanel, Panel drawingPanel,
                                           int initialWidth, int initialHeight,
                                           ColumnWidthRenderingStyle widthRenderingStyle)
        {
            // Adjust the width.
            if (widthRenderingStyle == ColumnWidthRenderingStyle.Truncating ||
                holderPanel.ClientSize.Width > initialWidth)
            {
                drawingPanel.Width = holderPanel.ClientSize.Width;
            }
            else
            {
                drawingPanel.Width = initialWidth;
            }

            // Adjust the height.
            if (holderPanel.ClientSize.Height > initialHeight)
            {
                drawingPanel.Height = holderPanel.ClientSize.Height;
            }
            else
            {
                drawingPanel.Height = initialHeight;
            }
        }

        /// <summary>
        /// Update the position and size of the given panel and
        /// invalidates panel if size changed.
        /// </summary>
        /// <param name="panel">The panel to update.</param>
        /// <param name="x">The new x coordinate.</param>
        /// <param name="y">The new y coordinate.</param>
        /// <param name="width">The new width.</param>
        /// <param name="height">The new height.</param>
        public static void UpdatePanel(Panel panel, int x, int y, int width, int height)
        {
            bool update = (panel.Width != width || panel.Height != height);
            panel.Left = x;
            panel.Top = y;
            panel.Width = width;
            panel.Height = height;
            if(update)
            {
                panel.Invalidate();
            }
        }

        /// <summary>
        /// Deallocates resource for child controls recursively.
        /// </summary>
        /// <param name="control">Control which contains child controls to release.</param>
        public static void ReleaseChildControls(Control control)
        {
            foreach (Control childControl in control.Controls)
            {
                ReleaseChildControls(childControl);
                childControl.Dispose();
            }
            control.Controls.Clear();
        }

        /// <summary>
        /// Copies list of strings.
        /// </summary>
        /// <param name="listSource">List to copy.</param>
        /// <returns>Copy of the given list, or null if given list is null.</returns>
        public static IList<string> GetStringListCopy(IList<string> listSource)
        {
            return listSource == null ? null : new List<string>(listSource);
        }

        /// <summary>
        /// Throws exception if null or empty element was found the in given list.
        /// </summary>
        /// <param name="stringList">List to check.</param>
        /// <param name="argName">Name of the list argument.</param>
        /// <exception cref="ArgumentException">
        /// If null or empty element was found the in given list.
        /// </exception>
        public static void CheckStringListForNullAndEmptyElements(IList<string> stringList, string argName)
        {
            if (stringList != null && argName != null)
            {
                foreach (string s in stringList)
                {
                    if (s == null || s.Trim().Length == 0)
                    {
                        throw new ArgumentException(
                            string.Format("Collection '{0}' should not contain null or empty strings.", argName),
                            argName);
                    }
                }
            }
        }

        /// <summary>
        /// Returns given string or null if given string is empty.
        /// </summary>
        /// <param name="s">String to process.</param>
        /// <returns>Given string or null if given string is empty</returns>
        public static string GetNullIfEmpty(string s)
        {
            return (s != null && s.Trim().Length == 0) ? null : s;
        }

        #region Helper classes to handle child panels painting

        /// <summary>
        /// Private helper class to draw month header panel.
        /// </summary>
        /// <remarks>Thread Safety: This class is immutable and thread-safe.</remarks>
        private class MonthHeaderPaintHandler
        {
            /// <summary>
            /// The first day of the month.
            /// </summary>
            private readonly DateTime date;

            /// <summary>
            /// Indicates whether to highlight this month with different color.
            /// </summary>
            private readonly bool highlightMonth;

            /// <summary>
            /// The font size to write name of the month.
            /// </summary>
            private readonly int monthNameFontSize;

            /// <summary>
            /// The margin of the text in the panel.
            /// </summary>
            private readonly int textMargin;

            /// <summary>
            /// Creates a new instance of the class.
            /// </summary>
            /// <param name="date">The first day of the month.</param>
            /// <param name="highlightMonth">Indicates whether to highlight this month with different color.</param>
            /// <param name="monthNameFontSize">The font size to write name of the month.</param>
            /// <param name="textMargin">The margin of the text in the panel.</param>
            public MonthHeaderPaintHandler(DateTime date, bool highlightMonth, int monthNameFontSize, int textMargin)
            {
                this.date = date;
                this.highlightMonth = highlightMonth;
                this.monthNameFontSize = monthNameFontSize;
                this.textMargin = textMargin;
            }

            /// <summary>
            /// Processes <see cref="Control.Paint"/> event for the month header panel.
            /// </summary>
            /// <param name="sender">The sender of the event.</param>
            /// <param name="e">The event, containing the graphic.</param>
            public void OnPaint(object sender, PaintEventArgs e)
            {
                Panel monthPanel = (Panel)sender;
                Graphics g = e.Graphics;

                Font monthNameFont = new Font(DEFAULT_FONT_NAME, monthNameFontSize, FontStyle.Bold);
                Brush monthNameBrush = new SolidBrush(Color.Black);

                if (highlightMonth && date.Year == DateTime.Now.Year && date.Month == DateTime.Now.Month)
                {
                    g.FillRectangle(thisMonthBrush, monthPanel.ClientRectangle);
                }
                g.DrawRectangle(borderPen, 0, 0, monthPanel.ClientSize.Width - 1, monthPanel.ClientSize.Height - 1);

                string monthName = date.ToString(MONTH_NAME_FORMAT_STRING);
                float offset = (monthPanel.ClientSize.Width - g.MeasureString(monthName, monthNameFont).Width) / 2;
                g.DrawString(monthName, monthNameFont, monthNameBrush, offset, textMargin);
            }
        }

        /// <summary>
        /// Private helper class to draw week header.
        /// </summary>
        /// <remarks>Thread Safety: This class is immutable and thread-safe.</remarks>
        private class WeekdaysPaintHandler
        {
            /// <summary>
            /// The date of the first day in the week.
            /// </summary>
            private readonly DateTime viewDate;

            /// <summary>
            /// The height of the header row.
            /// </summary>
            private readonly int headerRowHeight;

            /// <summary>
            /// The font size of the header.
            /// </summary>
            private readonly int headerFontSize;

            /// <summary>
            /// The margin of the text in the panel.
            /// </summary>
            private readonly int textMargin;

            /// <summary>
            /// Creates a new instance of the class.
            /// </summary>
            /// <param name="viewDate">The date of the first day in the week.</param>
            /// <param name="headerRowHeight">The height of the header row.</param>
            /// <param name="headerFontSize">The font size of the header.</param>
            /// <param name="textMargin">The margin of the text in the panel.</param>
            public WeekdaysPaintHandler(DateTime viewDate, int headerRowHeight, int headerFontSize, int textMargin)
            {
                this.viewDate = viewDate;
                this.headerRowHeight = headerRowHeight;
                this.headerFontSize = headerFontSize;
                this.textMargin = textMargin;
            }

            /// <summary>
            /// Processes <see cref="Control.Paint"/> event for the week header panel.
            /// </summary>
            /// <param name="sender">The sender of the event.</param>
            /// <param name="e">The event, containing the graphic.</param>
            public void OnPaint(object sender, PaintEventArgs e)
            {
                Panel weekPanel = (Panel)sender;
                Graphics g = e.Graphics;
                Font headerFont = new Font(DEFAULT_FONT_NAME, headerFontSize, FontStyle.Bold);

                float columnWidth = weekPanel.Width/7;

                for (int i = 0; i < 7; i++)
                {
                    // The background:
                    g.FillRectangle(headerBackBrush, columnWidth*i, 0, columnWidth + 1, headerRowHeight);
                    // The text:
                    g.DrawString(viewDate.AddDays(i).ToString(WEEKDAY_FORMAT_STRING), headerFont, headerBrush,
                                 columnWidth*i + textMargin, textMargin);
                }
            }
        }

        /// <summary>
        /// Private helper class to draw day cell.
        /// </summary>
        /// <remarks>Thread Safety: This class is immutable and thread-safe.</remarks>
        private class DayPaintHandler
        {
            /// <summary>
            /// The font size to write date.
            /// </summary>
            private readonly int dateFontSize;

            /// <summary>
            /// The date of this day.
            /// </summary>
            private readonly DateTime date;

            /// <summary>
            /// The margin of the date label.
            /// </summary>
            private readonly int dateLabelMargin;

            /// <summary>
            /// Brush to draw the background.
            /// </summary>
            private readonly Brush backColorBrush;

            /// <summary>
            /// The height of the cell.
            /// </summary>
            private readonly float height;

            /// <summary>
            /// The height of the date label.
            /// </summary>
            private readonly int dateLabelHeight;

            /// <summary>
            /// Collection of the calendar entries grouped by date.
            /// </summary>
            private readonly IDictionary<DateTime, IDictionary<CalendarEntryType, int>> sortedEntryTypes;

            /// <summary>
            /// The default height of each color bar.
            /// </summary>
            private readonly float defaultBarHeight;

            /// <summary>
            /// The parent calendar view of this panel.
            /// </summary>
            private readonly AbstractCalendarView view;

            /// <summary>
            /// Creates a new instance of the class.
            /// </summary>
            /// <param name="dateFontSize">The font size to write date.</param>
            /// <param name="date">The date of this day.</param>
            /// <param name="dateLabelMargin">The margin of the date label.</param>
            /// <param name="backColorBrush">Brush to draw the background.</param>
            /// <param name="height">The height of the cell.</param>
            /// <param name="dateLabelHeight">The height of the date label.</param>
            /// <param name="sortedEntryTypes">Collection of the calendar entries grouped by date.</param>
            /// <param name="defaultBarHeight">The default height of each color bar.</param>
            /// <param name="view">The parent calendar view of this panel.</param>
            public DayPaintHandler(int dateFontSize, DateTime date, int dateLabelMargin, Brush backColorBrush,
                                   float height, int dateLabelHeight,
                                   IDictionary<DateTime, IDictionary<CalendarEntryType, int>> sortedEntryTypes,
                                   float defaultBarHeight, AbstractCalendarView view)
            {
                this.view = view;
                this.defaultBarHeight = defaultBarHeight;
                this.sortedEntryTypes = sortedEntryTypes;
                this.dateLabelHeight = dateLabelHeight;
                this.height = height;
                this.backColorBrush = backColorBrush;
                this.dateFontSize = dateFontSize;
                this.date = date;
                this.dateLabelMargin = dateLabelMargin;
            }

            /// <summary>
            /// Processes <see cref="Control.Paint"/> event for one day panel.
            /// </summary>
            /// <param name="sender">The sender of the event.</param>
            /// <param name="e">The event, containing the graphic.</param>
            public void OnPaint(object sender, PaintEventArgs e)
            {
                Panel dayPanel = (Panel)sender;
                Graphics g = e.Graphics;

                Font dateFont = new Font(DEFAULT_FONT_NAME, dateFontSize);

                // Draw the background:
                g.FillRectangle(backColorBrush, dayPanel.ClientRectangle);

                // Draw the border:
                g.DrawRectangle(borderPen, 0, 0, dayPanel.ClientSize.Width - 1, dayPanel.ClientSize.Height - 1);

                // Draw the date:
                g.DrawString(date.Day.ToString(), dateFont, dateTextBrush, dateLabelMargin, dateLabelMargin);

                float y = dateLabelHeight;

                if (backColorBrush != otherMonthBackBrush && sortedEntryTypes.ContainsKey(date))
                {
                    int typeCount = sortedEntryTypes[date].Count;

                    float barHeight = defaultBarHeight;
                    if (typeCount*barHeight > height)
                    {
                        barHeight = height/typeCount;
                    }

                    // Draw the color bar:
                    foreach (CalendarEntryType entryType in sortedEntryTypes[date].Keys)
                    {
                        g.FillRectangle(new SolidBrush(view.GetColor(entryType)), 1, y + 1,
                            dayPanel.ClientSize.Width - 2, barHeight - 2);
                        y += barHeight;
                    }
                }
            }
        }

        #endregion

    }
}